/*
 * SeqCompView.java
 */

package org.elkastud;

import java.awt.GridLayout;
import org.elkastud.model.MatchResult3;
import org.jdesktop.application.Action;
import org.jdesktop.application.ResourceMap;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.application.FrameView;
import org.jdesktop.application.TaskMonitor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.swing.Timer;
import javax.swing.Icon;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import org.elkastud.fasta.FastaIO;
import org.elkastud.fasta.FastaSequence;
import org.elkastud.util.MFastaFileFilter;

/**
 * The application's main frame.
 */
public class SeqCompView extends FrameView {

    public SeqCompView(SingleFrameApplication app) {
        super(app);

        initComponents();
        initAppComponents();

        // status bar initialization - message timeout, idle icon and busy animation, etc
        ResourceMap resourceMap = getResourceMap();
        int messageTimeout = resourceMap.getInteger("StatusBar.messageTimeout");
        messageTimer = new Timer(messageTimeout, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                statusMessageLabel.setText("");
            }
        });
        messageTimer.setRepeats(false);
        int busyAnimationRate = resourceMap.getInteger("StatusBar.busyAnimationRate");
        for (int i = 0; i < busyIcons.length; i++) {
            busyIcons[i] = resourceMap.getIcon("StatusBar.busyIcons[" + i + "]");
        }
        busyIconTimer = new Timer(busyAnimationRate, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                busyIconIndex = (busyIconIndex + 1) % busyIcons.length;
                statusAnimationLabel.setIcon(busyIcons[busyIconIndex]);
            }
        });
        idleIcon = resourceMap.getIcon("StatusBar.idleIcon");
        statusAnimationLabel.setIcon(idleIcon);
        progressBar.setVisible(false);
        menuSave.setEnabled(false);
        menuStart.setEnabled(false);

        // connecting action tasks to status bar via TaskMonitor
        TaskMonitor taskMonitor = new TaskMonitor(getApplication().getContext());
        taskMonitor.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                String propertyName = evt.getPropertyName();
                if ("started".equals(propertyName)) {
                    if (!busyIconTimer.isRunning()) {
                        statusAnimationLabel.setIcon(busyIcons[0]);
                        busyIconIndex = 0;
                        busyIconTimer.start();
                        menuBar.setEnabled(false);
                    }
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(true);
                } else if ("done".equals(propertyName)) {
                    busyIconTimer.stop();
                    statusAnimationLabel.setIcon(idleIcon);
                    progressBar.setVisible(false);
                    progressBar.setValue(0);
                    menuBar.setEnabled(true);
                    menuSave.setEnabled(true);
                } else if ("message".equals(propertyName)) {
                    String text = (String)(evt.getNewValue());
                    statusMessageLabel.setText((text == null) ? "" : text);
                    messageTimer.restart();
                } else if ("progress".equals(propertyName)) {
                    int value = (Integer)(evt.getNewValue());
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(false);
                    progressBar.setValue(value);
                }
            }
        });
    }

    @Action
    /**
     * action to show about box
     */
    public void showAboutBox() {
        if (aboutBox == null) {
            JFrame mainFrame = SeqCompApp.getApplication().getMainFrame();
            aboutBox = new SeqCompAboutBox(mainFrame);
            aboutBox.setLocationRelativeTo(mainFrame);
        }
        SeqCompApp.getApplication().show(aboutBox);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        menuBar = new javax.swing.JMenuBar();
        javax.swing.JMenu fileMenu = new javax.swing.JMenu();
        menuStart = new javax.swing.JMenuItem();
        menuOpen = new javax.swing.JMenuItem();
        menuSave = new javax.swing.JMenuItem();
        menuMethod = new javax.swing.JMenu();
        menuNeedlemanWunch = new javax.swing.JRadioButtonMenuItem();
        menuSmithWaterman = new javax.swing.JRadioButtonMenuItem();
        javax.swing.JMenuItem menuExit = new javax.swing.JMenuItem();
        javax.swing.JMenu helpMenu = new javax.swing.JMenu();
        javax.swing.JMenuItem aboutMenuItem = new javax.swing.JMenuItem();
        statusPanel = new javax.swing.JPanel();
        javax.swing.JSeparator statusPanelSeparator = new javax.swing.JSeparator();
        statusMessageLabel = new javax.swing.JLabel();
        statusAnimationLabel = new javax.swing.JLabel();
        progressBar = new javax.swing.JProgressBar();
        menuMethodRadioGroup = new javax.swing.ButtonGroup();
        jScrollPane1 = new javax.swing.JScrollPane();
        mainPanel = new javax.swing.JPanel();
        jScrollPane2 = new javax.swing.JScrollPane();
        resultsPanel = new javax.swing.JPanel();

        menuBar.setName("menuBar"); // NOI18N

        org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(org.elkastud.SeqCompApp.class).getContext().getResourceMap(SeqCompView.class);
        fileMenu.setText(resourceMap.getString("fileMenu.text")); // NOI18N
        fileMenu.setName("fileMenu"); // NOI18N

        javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(org.elkastud.SeqCompApp.class).getContext().getActionMap(SeqCompView.class, this);
        menuStart.setAction(actionMap.get("startCompare")); // NOI18N
        menuStart.setText(resourceMap.getString("menuStart.text")); // NOI18N
        menuStart.setName("menuStart"); // NOI18N
        fileMenu.add(menuStart);

        menuOpen.setAction(actionMap.get("openFile")); // NOI18N
        menuOpen.setText(resourceMap.getString("menuOpen.text")); // NOI18N
        menuOpen.setName("menuOpen"); // NOI18N
        fileMenu.add(menuOpen);

        menuSave.setAction(actionMap.get("saveAction")); // NOI18N
        menuSave.setText(resourceMap.getString("menuSave.text")); // NOI18N
        menuSave.setName("menuSave"); // NOI18N
        fileMenu.add(menuSave);

        menuMethod.setText(resourceMap.getString("menuMethod.text")); // NOI18N
        menuMethod.setName("menuMethod"); // NOI18N

        menuNeedlemanWunch.setAction(actionMap.get("methodChoose")); // NOI18N
        menuMethodRadioGroup.add(menuNeedlemanWunch);
        menuNeedlemanWunch.setSelected(true);
        menuNeedlemanWunch.setText(resourceMap.getString("menuNeedlemanWunch.text")); // NOI18N
        menuNeedlemanWunch.setName("menuNeedlemanWunch"); // NOI18N
        menuMethod.add(menuNeedlemanWunch);

        menuSmithWaterman.setAction(actionMap.get("methodChoose")); // NOI18N
        menuMethodRadioGroup.add(menuSmithWaterman);
        menuSmithWaterman.setText(resourceMap.getString("menuSmithWaterman.text")); // NOI18N
        menuSmithWaterman.setName("menuSmithWaterman"); // NOI18N
        menuMethod.add(menuSmithWaterman);

        fileMenu.add(menuMethod);

        menuExit.setText(resourceMap.getString("menuExit.text")); // NOI18N
        fileMenu.add(menuExit);

        menuBar.add(fileMenu);

        helpMenu.setText(resourceMap.getString("helpMenu.text")); // NOI18N
        helpMenu.setName("helpMenu"); // NOI18N

        aboutMenuItem.setAction(actionMap.get("showAboutBox")); // NOI18N
        aboutMenuItem.setName("aboutMenuItem"); // NOI18N
        helpMenu.add(aboutMenuItem);

        menuBar.add(helpMenu);

        statusPanel.setName("statusPanel"); // NOI18N

        statusPanelSeparator.setName("statusPanelSeparator"); // NOI18N

        statusMessageLabel.setName("statusMessageLabel"); // NOI18N

        statusAnimationLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        statusAnimationLabel.setName("statusAnimationLabel"); // NOI18N

        progressBar.setName("progressBar"); // NOI18N

        javax.swing.GroupLayout statusPanelLayout = new javax.swing.GroupLayout(statusPanel);
        statusPanel.setLayout(statusPanelLayout);
        statusPanelLayout.setHorizontalGroup(
            statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(statusPanelSeparator, javax.swing.GroupLayout.DEFAULT_SIZE, 534, Short.MAX_VALUE)
            .addGroup(statusPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(statusMessageLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 364, Short.MAX_VALUE)
                .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(statusAnimationLabel)
                .addContainerGap())
        );
        statusPanelLayout.setVerticalGroup(
            statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(statusPanelLayout.createSequentialGroup()
                .addComponent(statusPanelSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(statusMessageLabel)
                    .addComponent(statusAnimationLabel)
                    .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(3, 3, 3))
        );

        jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        jScrollPane1.setName("jScrollPane1"); // NOI18N
        jScrollPane1.setPreferredSize(new java.awt.Dimension(0, 0));

        mainPanel.setName("mainPanel"); // NOI18N
        mainPanel.setOpaque(false);
        mainPanel.setPreferredSize(new java.awt.Dimension(0, 0));

        jScrollPane2.setName("jScrollPane2"); // NOI18N

        resultsPanel.setName("resultsPanel"); // NOI18N

        javax.swing.GroupLayout resultsPanelLayout = new javax.swing.GroupLayout(resultsPanel);
        resultsPanel.setLayout(resultsPanelLayout);
        resultsPanelLayout.setHorizontalGroup(
            resultsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 532, Short.MAX_VALUE)
        );
        resultsPanelLayout.setVerticalGroup(
            resultsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 252, Short.MAX_VALUE)
        );

        jScrollPane2.setViewportView(resultsPanel);

        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
        mainPanel.setLayout(mainPanelLayout);
        mainPanelLayout.setHorizontalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 534, Short.MAX_VALUE)
        );
        mainPanelLayout.setVerticalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 254, Short.MAX_VALUE)
        );

        jScrollPane1.setViewportView(mainPanel);

        setComponent(mainPanel);
        setMenuBar(menuBar);
        setStatusBar(statusPanel);
    }// </editor-fold>//GEN-END:initComponents

    @Action
    /**
     * method select action
     */
    public void methodChoose() {
        if (menuNeedlemanWunch.isSelected()) {
            ((SeqCompApp)this.getApplication()).setMethod(Method.NEEDLEMAN_WUNCH);
        } else {
            ((SeqCompApp)this.getApplication()).setMethod(Method.SMITH_WATERMAN);
        }
    }

    @Action
    /**
     * read input sequences
     */
    public void openFile() {
        this.fileChooser.setDialogType(JFileChooser.OPEN_DIALOG);
        this.fileChooser.setFileFilter(new MFastaFileFilter(false));
        int returnValue = this.fileChooser.showDialog(this.getFrame(), ADD_BUTTON);
        if (returnValue == JFileChooser.APPROVE_OPTION) {
            File file = this.fileChooser.getSelectedFile();
            List<FastaSequence> sequence = FastaIO.readFile(file);
            ((SeqCompApp)this.getApplication()).addSequence(sequence);
            menuStart.setEnabled(true);
            statusMessageLabel.setText("file opened succesfully");
            messageTimer.restart();
        }
    }

    @Action
    /**
     * save results
     */
    public void saveAction() {
        this.fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
        this.fileChooser.setFileFilter(new MFastaFileFilter(true));
        int returnValue = this.fileChooser.showDialog(this.getFrame(), SAVE_BUTTON);
        if (returnValue == JFileChooser.APPROVE_OPTION) {
            File file = this.fileChooser.getSelectedFile();
            try {
                FastaIO.saveFile(file, SeqCompApp.getApplication().getResult());
                JOptionPane.showMessageDialog(this.getFrame(), "File saved.", "Saved", JOptionPane.INFORMATION_MESSAGE);
            } catch (IOException e) {
                JOptionPane.showMessageDialog(this.getFrame(), "Cannot save file.", "Error", JOptionPane.ERROR_MESSAGE);
            } catch (Exception e) {
                JOptionPane.showMessageDialog(this.getFrame(), e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    @Action
    /**
     * Start compare action
     */
    public void startCompare() {
        StartDialog dialog = new StartDialog(this.getFrame(), true);
        SeqCompApp.getApplication().show(dialog);
    }

    /**
     * show compare results
     * @param resultList compare results
     */
    public void showResultPanel(List<MatchResult3> resultList) {
        this.resultsPanel.removeAll();
        this.resultsPanel.setLayout(new GridLayout(resultList.size(), 1));

        for (MatchResult3 result : resultList) {
            ResultPanel rPanel = new ResultPanel();
            this.resultsPanel.add(rPanel);
            rPanel.setSequences(result.getSequenceA(), result.getSequenceB(), result.getSequenceC());
        }
    }

    /**
     * init not generated components
     */
    private void initAppComponents() {
        this.fileChooser = new JFileChooser();
        if (this.menuNeedlemanWunch.isSelected()) {
            ((SeqCompApp)this.getApplication()).setMethod(Method.NEEDLEMAN_WUNCH);
        } else {
            ((SeqCompApp)this.getApplication()).setMethod(Method.SMITH_WATERMAN);
        }
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JPanel mainPanel;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JMenu menuMethod;
    private javax.swing.ButtonGroup menuMethodRadioGroup;
    private javax.swing.JRadioButtonMenuItem menuNeedlemanWunch;
    private javax.swing.JMenuItem menuOpen;
    private javax.swing.JMenuItem menuSave;
    private javax.swing.JRadioButtonMenuItem menuSmithWaterman;
    private javax.swing.JMenuItem menuStart;
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JPanel resultsPanel;
    private javax.swing.JLabel statusAnimationLabel;
    private javax.swing.JLabel statusMessageLabel;
    private javax.swing.JPanel statusPanel;
    // End of variables declaration//GEN-END:variables

    private final Timer messageTimer;
    private final Timer busyIconTimer;
    private final Icon idleIcon;
    private final Icon[] busyIcons = new Icon[15];
    private int busyIconIndex = 0;

    private JDialog aboutBox;

    private JFileChooser fileChooser;

    private static final String ADD_BUTTON = "Add";
    private static final String SAVE_BUTTON = "Save";

}
